package com.tjhzzx.hnceCore2024.model.schedule;


import com.tjhzzx.hnceCore2024.enums.BPeriodType;
import com.tjhzzx.hnceCore2024.history.HistoryManager;
import com.tjhzzx.hnceCore2024.history.entities.HistoryDataItem;
import com.tjhzzx.hnceCore2024.history.entities.HistoryDataMessage;
import com.tjhzzx.hnceCore2024.history.entities.HistoryDataUnit;
import com.tjhzzx.hnceCore2024.model.energy.BPointBinding;
import com.tjhzzx.hnceCore2024.util.TlsUtil;

import javax.baja.control.BNumericPoint;
import javax.baja.control.BNumericWritable;
import javax.baja.naming.BOrd;
import javax.baja.sys.*;
import javax.baja.util.BFolder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

/**
 * @author Luc
 * @version 1.0
 * @date 2021-07-09 9:55
 */
public class BScheduleJob extends BComponent {

    private static final Logger LOGGER = Logger.getLogger(BScheduleJob.class.getName());

    ////////////////////////////////////////////////////////////////
// Property "enabled"
////////////////////////////////////////////////////////////////

    /**
     * Slot for the {@code enabled} property.
     * @see #getEnabled
     * @see #setEnabled
     */
    public static final Property enabled = newProperty(0, false, null);

    /**
     * Get the {@code enabled} property.
     * @see #enabled
     */
    public boolean getEnabled() { return getBoolean(enabled); }

    /**
     * Set the {@code enabled} property.
     * @see #enabled
     */
    public void setEnabled(boolean v) { setBoolean(enabled, v, null); }

    ////////////////////////////////////////////////////////////////
// Property "interval"
////////////////////////////////////////////////////////////////

    /**
     * Slot for the {@code interval} property.
     * @see #getInterval
     * @see #setInterval
     */
    public static final Property interval = newProperty(Flags.READONLY, BRelTime.makeSeconds(1), BFacets.make(BFacets.MIN, BRelTime.makeSeconds(1)));

    /**
     * Get the {@code interval} property.
     * @see #interval
     */
    public BRelTime getInterval() { return (BRelTime)get(interval); }

    /**
     * Set the {@code interval} property.
     * @see #interval
     */
    public void setInterval(BRelTime v) { set(interval, v, null); }

    public static final Property start = newProperty(0, BAbsTime.now(), BFacets.make(BFacets.MIN, BAbsTime.make(1000)));

    public BAbsTime getStart() { return (BAbsTime)get(start); }

    public void setStart(BAbsTime v) { set(start, v, null); }
    ////////////////////////////////////////////////////////////////
// Action "intervalElapsed"
////////////////////////////////////////////////////////////////

    /**
     * Slot for the {@code intervalElapsed} action.
     * @see #intervalElapsed()
     */
    public static final Action intervalElapsed = newAction(Flags.HIDDEN, null);

    /**
     * Invoke the {@code intervalElapsed} action.
     * @see #intervalElapsed
     */
    public void intervalElapsed() { invoke(intervalElapsed, null, null); }

    @Override
    public void started() throws Exception {
        super.started();
        if (isValidInterval(getInterval())) {
            // make sure the intervals match at startup
//            scheduleCollection(BAbsTime.make());
            LOGGER.info("BScheduleJob started:" + isValidInterval(getInterval()));
            scheduleCollection(getStart());
        }
    }

    @Override
    public void changed(Property property, Context context) {
        super.changed(property, context);
        if("enabled".equals(property.getName())) {
            LOGGER.info("changed:" + getBoolean(property));
            if(getBoolean(property)) {
                try {
                    this.started();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                synchronized (lock) {
                    if (ticket != null) {
                        ticket.cancel();
                        ticket = null;
                    }
                }
            }
        }
    }

    /**
     * 验证是否为一个合法的周期时间
     */
    private boolean isValidInterval(BRelTime interval) {
        return interval.getMillis() > 0;
    }

    /**
     * 开始周期采集定时器
     */
    private void scheduleCollection(BAbsTime from) {
        BAbsTime startTime;
        BRelTime interval = getInterval();
        LOGGER.info("BScheduleJob scheduleCollection1:" + from);
        if (!isValidInterval(interval) || !getEnabled()) {
            LOGGER.info("BScheduleJob scheduleCollection error:" + isValidInterval(interval) + ", " + getEnabled());
            return;
        }

        synchronized (lock) {
            if (ticket != null) {
                ticket.cancel();
                ticket = null;
            }
            startTime = BAbsTime.make((BAbsTime.now().getMillis() / interval.getMillis()) * interval.getMillis());
            LOGGER.info("BScheduleJob scheduleCollection2:" + startTime + ", " + from);
            while (!startTime.isAfter(from))
                startTime = startTime.add(interval);
//            LOGGER.info("BScheduleJob scheduleCollection2:" + startTime);
            if (isRunning())
                ticket = Clock.schedulePeriodically(this, startTime, interval, intervalElapsed, null);
        }
    }


    /**
     * Receive notification that the current interval has elasped.  This
     * causes a record to be written.
     */
    public void doIntervalElapsed() {
        BAbsTime now = BAbsTime.now();
//        LOGGER.severe("BScheduleJob doIntervalElapsed1:" + now.getSecond());
        if(now.isAfter(getStart())) {
            if(now.getSecond() == 0) {
                LOGGER.severe("BScheduleJob doIntervalElapsed:" + now);
            }
            job();
        }
    }

    public static final Property points = newProperty(0, new BFolder(), null);

    public BFolder getPoints() {
        return (BFolder) get(points);
    }

    public void setPoints(BFolder v) {
        set(points, v, null);
    }

    public void job() {
        Date now = new Date();
        String str = TlsUtil.utilDate2String(now, "HH:mm:ss", false);
        if("00:00:00".equals(str)) {
            LOGGER.info("BScheduleJob job invoke:" + now);
        }
        BFolder points = getPoints();
        BPointBinding[] pointBindings = points.getChildren(BPointBinding.class);
        if(null != pointBindings) {
            for(BPointBinding pointBinding : pointBindings) {
                BPeriodType periodType = pointBinding.getPeriodType();
                String start = TlsUtil.utilDate2String(now, false);
                HistoryDataMessage request = new HistoryDataMessage();
                Date from = now;
                if(periodType == BPeriodType.day) {
                    str = TlsUtil.utilDate2String(now, "HH:mm:ss", false);
                    if(!"00:00:01".equals(str)) {
                        continue;
                    }
                    from = TlsUtil.getLastDate(now, 0, -1);
                } else if(periodType == BPeriodType.month) {
                    str = TlsUtil.utilDate2String(now, "dd HH:mm:ss", false);
                    if(!"01 00:00:01".equals(str)) {
                        continue;
                    }
                    from = TlsUtil.getLastDate(now, 0, -1);
                } else if(periodType == BPeriodType.year) {
                    str = TlsUtil.utilDate2String(now, "MM-dd HH:mm:ss", false);
                    if(!"01-01 00:00:01".equals(str)) {
                        continue;
                    }
                    from = TlsUtil.getLastDate(now, 0, -1);
                }
                BOrd origin = pointBinding.getOriginOrd();
                BOrd history = pointBinding.getHistoryOrd();
                BOrd target = pointBinding.getTargetOrd();
                BComponent component = (BComponent) origin.get();
                LOGGER.severe("BScheduleJob job start:" + now + ", " + origin);
                Type type = component.getType();
                double currentValue = 0;
                if(type.is(BNumericWritable.TYPE)) {
                    BNumericWritable point = (BNumericWritable)component;
                    currentValue = point.getOut().getValue();
                } else if(type.is(BNumericPoint.TYPE)) {
                    BNumericPoint point = (BNumericPoint)component;
                    currentValue = point.getOut().getValue();
                }
                LOGGER.severe("BScheduleJob job currentValue:" + currentValue);
                request.startTime = TlsUtil.utilDate2String(from, false);
                request.endTime = start;
                LOGGER.severe("BScheduleJob job startTime=" + request.startTime + ", endTime=" + request.endTime);
                List<HistoryDataItem> items = new ArrayList<>();
                HistoryDataItem historyDataItem = new HistoryDataItem();
                historyDataItem.name = origin.toString();
                historyDataItem.ord = history.toString().replace("history:", "");
                items.add(historyDataItem);
                request.items = items;
                LOGGER.severe("BScheduleJob job name=" + historyDataItem.name + ", ord=" + historyDataItem.ord);
                HistoryDataMessage result = HistoryManager.searchHistoryData(request);
                if(null != result && null != result.getItems() && result.getItems().size() > 0) {
                    HistoryDataItem item = result.getItems().get(0);
                    List<HistoryDataUnit> units = item.getUnits();
                    if(null != units && units.size() > 0) {
                        LOGGER.severe("BScheduleJob job first=" + units.get(0).value + ", end=" + units.get(units.size()-1).value);
                        double last = Double.parseDouble(units.get(0).value);
                        LOGGER.severe("BScheduleJob job diff=" + (currentValue - last));
                        component = (BComponent) target.get();
                        type = component.getType();
                        if(type.is(BNumericWritable.TYPE)) {
                            BNumericWritable point = (BNumericWritable)component;
                            point.set(BDouble.make((currentValue - last)));
                        }
                    }
                }
            }
        }
    }



    @Override
    public Type getType() { return TYPE; }
    public static final Type TYPE = Sys.loadType(BScheduleJob.class);

    private Clock.Ticket ticket = null;
    private final Object lock = new Object();
}
